
#include "def.h"
#include "HttpConnection.h"
#include <curl/curl.h>

#define DOWNLOAD_FILE	"test.dat"

#if defined(USE_LIBCURL) && (defined(_WIN32) || defined(_WIN64))
#pragma comment(lib, "libcurl.lib")
#else // USE_LIBCURL
#pragma comment(lib, "Urlmon.lib")
#endif // USE_LIBCURL

HttpConnection::HttpConnection(void) : 
	m_nResCode(0)
	, m_lpResData(0)
#ifdef USE_LIBCURL
	, m_strProxyHost(0)
	, m_strProxyPass(0)
	, m_strProxyUser(0)
#endif // USE_LIBCURL
{
}

HttpConnection::~HttpConnection(void)
{
#ifdef USE_LIBCURL
	clearData();
	STR_DEL(m_strProxyHost);
	STR_DEL(m_strProxyUser);
	STR_DEL(m_strProxyPass);
#endif //USE_LIBCURL
}

void HttpConnection::clearData()
{
	m_nResCode = 0;
	STR_DEL(m_lpResData);
}

void HttpConnection::cleanup()
{
#ifdef USE_LIBCURL
	// clean curl object
	if (0 != m_objCurl)
	{
		curl_easy_cleanup(m_objCurl);
		m_objCurl = 0;
	}

	// close temporary file for response content
	if (0 != m_tmpBody)
	{
		fclose(m_tmpBody);
		m_tmpBody = 0;
	}

	// close temporary file for response header
	if (0 != m_tmpHeader)
	{
		fclose(m_tmpHeader);
		m_tmpHeader = 0;
	}
#else // USE_LIBCURL
	::DeleteFileA(DOWNLOAD_FILE);
#endif // USE_LIBCURL
}

#ifdef USE_LIBCURL
void HttpConnection::setProxy(const char *ip, const int & port, const char * user, const char * pass)
{
	STR_CPY(m_strProxyHost, ip);
	STR_CPY(m_strProxyUser, user);
	STR_CPY(m_strProxyPass, pass);
	m_nProxyPort = port;
}

const int HttpConnection::prepare(const int bAutoRedirect, const int bIgnoreSecurityCheck)
{
	// create curl object
	m_objCurl = curl_easy_init();
	if (0 == m_objCurl)
		return 1;

	// set FILE to store response's headers and content
	m_tmpBody = tmpfile();
	m_tmpHeader = tmpfile();
	if (0 == m_tmpBody || 0 == m_tmpHeader)
		return 2;

#if 0
	curl_easy_setopt(m_objCurl, CURLOPT_ENCODING, "gzip");
	curl_easy_setopt(m_objCurl, CURLOPT_HTTP_CONTENT_DECODING, 1L);
#endif

	// set time out
	curl_easy_setopt(m_objCurl, CURLOPT_TIMEOUT, 60);
	curl_easy_setopt(m_objCurl, CURLOPT_CONNECTTIMEOUT, 60);

	// set output buffer
	curl_easy_setopt(m_objCurl, CURLOPT_FILE, m_tmpBody);
	curl_easy_setopt(m_objCurl, CURLOPT_WRITEHEADER, m_tmpHeader);

	// no need of progress meter
	curl_easy_setopt(m_objCurl, CURLOPT_NOPROGRESS, 1L);

	// auto redirect
	if (bAutoRedirect)
	{
		curl_easy_setopt(m_objCurl, CURLOPT_FOLLOWLOCATION, 1L);
		curl_easy_setopt(m_objCurl, CURLOPT_AUTOREFERER, 1L);
	}

	// ignore SSL security check
	if (bIgnoreSecurityCheck)
	{
		curl_easy_setopt(m_objCurl, CURLOPT_SSL_VERIFYPEER, 0L);
		curl_easy_setopt(m_objCurl, CURLOPT_SSL_VERIFYHOST, 0L);
	}

	// set proxy
	if (m_strProxyHost && strlen(m_strProxyHost) > 0 && m_nProxyPort != 0)
	{
		curl_easy_setopt(m_objCurl, CURLOPT_PROXY, m_strProxyHost);
		curl_easy_setopt(m_objCurl, CURLOPT_PROXYPORT, m_nProxyPort);
		if (m_strProxyUser && strlen(m_strProxyUser) > 0)
			curl_easy_setopt(m_objCurl, CURLOPT_PROXYUSERNAME, m_strProxyUser);
		if (m_strProxyPass && strlen(m_strProxyPass) > 0)
			curl_easy_setopt(m_objCurl, CURLOPT_PROXYPASSWORD, m_strProxyPass);
		curl_easy_setopt(m_objCurl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
	}

	// done
	return 0;
}

#endif // USE_LIBCURL

const int HttpConnection::requestGet(const char * url, const int bAutoRedirect, const int bIgnoreSecurityCheck)
{
#ifdef USE_LIBCURL
	// prepare a request
	if (prepare(bAutoRedirect, bIgnoreSecurityCheck))
		return 1;

	// set URL
	if (CURLE_OK != curl_easy_setopt(m_objCurl, CURLOPT_URL, url))
		return 2;

	// set to GET method
	if (CURLE_OK != curl_easy_setopt(m_objCurl, CURLOPT_HTTPGET, 1L))
		return 3;

	// perform request
	if (CURLE_OK != curl_easy_perform(m_objCurl))
		return 4;
#else // USE_LIBCURL
	m_nResCode = 403;
	if (S_OK != URLDownloadToFileA(0, url, DOWNLOAD_FILE, 0, 0))
		return 1;

	m_nResCode = 0;
#endif // USE_LIBCURL

	// check the result and process the response
	if (process())
		return 5;

	// clean up the request
	cleanup();

	// done
	return 0;
}

const int HttpConnection::requestPost(const char *url, const char **fields, const char **values, const int bAutoRedirect, const int bIgnoreSecurityCheck)
{
#ifdef USE_LIBCURL
	// prepare a request
	if (prepare(bAutoRedirect, bIgnoreSecurityCheck))
		return 1;

	// set URL
	if (CURLE_OK != curl_easy_setopt(m_objCurl, CURLOPT_URL, url))
		return 2;

	// set to POST method
	if (CURLE_OK != curl_easy_setopt(m_objCurl, CURLOPT_HTTPPOST, 1L))
		return 3;

	static const char buf[] = "Expect:";
	struct curl_httppost *formpost=NULL;
	struct curl_httppost *lastptr=NULL;
	struct curl_slist *headerlist=NULL;
	headerlist = curl_slist_append(headerlist, buf);

	int nCount = 0;
	while (fields[nCount])
	{
		curl_formadd(&formpost, &lastptr,
			CURLFORM_COPYNAME, fields[nCount],
			CURLFORM_COPYCONTENTS, values[nCount],
			CURLFORM_END);
		nCount++;
	}

	/* only disable 100-continue header if explicitly requested */ 
	if (CURLE_OK != curl_easy_setopt(m_objCurl, CURLOPT_HTTPHEADER, headerlist))
		return 4;

	// set the form to cURL object
	if (CURLE_OK != curl_easy_setopt(m_objCurl, CURLOPT_HTTPPOST, formpost))
		return 5;

	// perform request
	if (CURLE_OK != curl_easy_perform(m_objCurl))
		return 6;

	/* then cleanup the formpost chain */ 
	curl_formfree(formpost);

	/* free slist */ 
	curl_slist_free_all (headerlist);
#else // USE_LIBCURL

#endif // USE_LIBCURL

	// check the result and process the response
	if (process())
		return 7;

	// clean up the request
	cleanup();

	// done
	return 0;
}

const char * HttpConnection::response()
{
	return m_lpResData;
}

const int HttpConnection::responseSize()
{
	return m_nResSize;
}

const int HttpConnection::responseCode()
{
	return m_nResCode;
}

const int HttpConnection::process()
{
	// clear old data
	clearData();

#ifdef USE_LIBCURL
	// get response code
	if (CURLE_OK != curl_easy_getinfo(m_objCurl, CURLINFO_RESPONSE_CODE, &m_nResCode))
		return 1;

	// copy raw data
	m_nResSize = ftell(m_tmpBody);
	m_lpResData = (char*)malloc(m_nResSize + 1);
	fseek(m_tmpBody, 0, SEEK_SET);
	fread(m_lpResData, m_nResSize, 1, m_tmpBody);
	m_lpResData[m_nResSize] = 0;
#else // USE_LIBCURL
	FILE *fp = fopen(DOWNLOAD_FILE, "rb");
	if (0 == fp)
		return 1;

	// copy raw data
	fseek(fp, 0, SEEK_END);
	m_nResSize = ftell(fp);
	m_lpResData = (char*)malloc(m_nResSize + 1);
	fseek(fp, 0, SEEK_SET);
	fread(m_lpResData, m_nResSize, 1, fp);
	m_lpResData[m_nResSize] = 0;
	fclose(fp);

	::DeleteFile(DOWNLOAD_FILE);
#endif // USE_LIBCURL

	// done
	return 0;
}
